﻿using System;
using System.Collections.Generic;
using System.Text;
using PostSharp.Laos;
using System.Reflection;
using PostSharp.Extensibility;
using B4ALL.JITL.Contract;
using B4ALL.Common;

namespace B4ALL.JITL
{
    [Serializable]
    [MulticastAttributeUsage(MulticastTargets.Class | MulticastTargets.Struct)]
    public class JITLObjectAttribute : CompoundAspect, IRequirePostSharp
    {
        public override void ProvideAspects(object element, LaosReflectionAspectCollection collection)
        {
            // Get the target type.
            Type targetType = (Type)element;

            // On the type, add a Composition aspect to implement the INotifyPropertyChanged interface.
            collection.AddAspect(targetType, new AddCacheSingletonInterfaceAspect());

            // Add a OnMethodBoundaryAspect on each writable/readable non-static property.
            foreach (PropertyInfo property in targetType.UnderlyingSystemType.GetProperties())
            {
                if (property.DeclaringType == targetType)
                {
                    if (property.CanRead)
                    {
                        MethodInfo method = property.GetGetMethod();

                        if (!method.IsStatic)
                        {
                            collection.AddAspect(method, new OnJITLObjectGetAspect());
                        }
                    }

                    if (property.CanWrite)
                    {
                        MethodInfo method = property.GetSetMethod();

                        if (!method.IsStatic)
                        {
                            collection.AddAspect(method, new OnJITLObjectGetAspect());
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Object that implements this aspect must have all their properties readable & writable
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        //public override bool CompileTimeValidate(object element)
        //{

        //    Type targetType = (Type)element;

        //    foreach (PropertyInfo property in targetType.UnderlyingSystemType.GetProperties())
        //    {
        //        if (property.DeclaringType == targetType && (!property.CanRead || !property.CanWrite))
        //        {
        //            throw new Exception(String.Format("Property {0} in class of type {1} is not readable or writable (mandatory for JSONObject)", property.Name, targetType.Name));
        //            return false;
        //        }
        //    }
        //    return false;
        //}

        /// <summary>
        /// Returns the requirements of the current attribute in the post-compilation process.
        /// </summary>
        /// <returns>A <see cref="PostSharpRequirements"/> object meaning that the 
        /// task named <b>SampleTrace</b> is required.</returns>
        public override PostSharpRequirements GetPostSharpRequirements()
        {
            PostSharpRequirements requirements = new PostSharpRequirements();
            requirements.Tasks.Add("B4ALL.JITL.Operator.Task");
            requirements.Tasks.Add("PostSharp.Laos");
            requirements.PlugIns.Add("PostSharp.Laos");
            return requirements;
        }

    }

    [Serializable]
    internal class OnJITLObjectGetAspect : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
            IJITLObject jitlObject = (IJITLObject)eventArgs.Instance;
            jitlObject.Load(eventArgs.Instance);
        }
    }

    [Serializable]
    internal class AddCacheSingletonInterfaceAspect : CompositionAspect
    {
        public override object CreateImplementationObject(PostSharp.Laos.InstanceBoundLaosEventArgs eventArgs)
        {
            return new JITLObject();
        }

        public override Type GetPublicInterface(Type containerType)
        {
            return typeof(IJITLObject);
        }

    }

}
